/**
 * Copyright (C) 2010 David Simonek <dafesimonek@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * JavaZoomPlayer.java
 *
 * Created on Jul 12, 2010, 5:38:15 PM
 */

package org.dafe.partyband.mediaplayer;

import java.io.File;
import java.util.Hashtable;
import java.util.Map;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javazoom.jlgui.basicplayer.BasicController;
import javazoom.jlgui.basicplayer.BasicPlayer;
import javazoom.jlgui.basicplayer.BasicPlayerEvent;
import javazoom.jlgui.basicplayer.BasicPlayerException;
import javazoom.jlgui.basicplayer.BasicPlayerListener;
import org.dafe.partyband.db.api.ISong;
import org.dafe.partyband.db.api.ISongResource;
import org.dafe.partyband.importer.api.ResourceTypeUtils;
import org.dafe.partyband.services.api.IDurationObtainer;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;

/**
 *
 * @author dafe
 */
public class JavaZoomPlayer extends javax.swing.JPanel implements IMediaPlayer, BasicPlayerListener {

    private final BasicPlayer player;
    private File content;
    private boolean wasPlaying = false;
    private boolean sliderDragged = false;
    private float bytesPerSecond;
    private File oldContent;

    /** Creates new form JavaZoomPlayer */
    public JavaZoomPlayer() {
        initComponents();
    
        player = new BasicPlayer();
        player.addBasicPlayerListener(this);
    }

    @Override
    public void setContent(File content, String songName, String author) {
        this.content = content;
        songLabel.setText(NbBundle.getMessage(JavaZoomPlayer.class, "JavaZoomPlayer.songLabel.text",
                songName + " - " + author));
        songLabel.setEnabled(true);
        songSlider.setEnabled(true);
    }

    @Override
    public void play() {
        // BasicPlayer is a BasicController.
        BasicController control = (BasicController) player;
        // Register BasicPlayerTest to BasicPlayerListener events.
        // It means that this object will be notified on BasicPlayer
        // events such as : opened(...), progress(...), stateUpdated(...)
        //player.addBasicPlayerListener(this);

        try { // Open file, or URL or Stream (shoutcast, icecast) to play.
            control.open(content);
            // control.open(new URL("http://yourshoutcastserver.com:8000"));

            // Start playback in a thread.
            control.play();

            // If you want to pause/resume/pause the played file then
            // write a Swing player and just call control.pause(),
            // control.resume() or control.stop().
            // Use control.seek(bytesToSkip) to seek file
            // (i.e. fast forward and rewind). seek feature will
            // work only if underlying JavaSound SPI implements
            // skip(...). True for MP3SPI and SUN SPI's
            // (WAVE, AU, AIFF). // Set Volume (0 to 1.0). control.setGain(0.85);
            // Set Pan (-1.0 to 1.0).
            //control.setPan(0.0);
        } catch (BasicPlayerException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    public JComponent getControllerPanel() {
        return this;
    }

/**        try {
            final Player player = new Player(content.toURL().openStream());
            RequestProcessor.getDefault().post(new Runnable() {
                @Override
                public void run() {
                    try {
                        player.play();
                    } catch (JavaLayerException ex) {
                        Exceptions.printStackTrace(ex);
                    }
                }
            });
        } catch (JavaLayerException ex) {
            Exceptions.printStackTrace(ex);
        } catch (MalformedURLException ex) {
            Exceptions.printStackTrace(ex);
        } catch (IOException ex) {
            Exceptions.printStackTrace(ex);
        }
    } */
    
    public void opened(Object stream, final Map properties) {
        System.out.println("Opened, thread: " + Thread.currentThread().toString());

        if (oldContent == content) {
            return;
        }
        oldContent = content;

        Float fps = (Float)properties.get("audio.framerate.fps");
        if (fps == null) {
            fps = (Float)properties.get("mp3.framerate.fps");
        }
        Integer fsize = (Integer)properties.get("audio.framesize.bytes");
        if (fsize == null) {
            fsize = (Integer)properties.get("mp3.framesize.bytes");
        }
        bytesPerSecond = fps * fsize;

        int duration = JavaZoomUtils.obtainDuration(stream, properties);

        songSlider.setMaximum(duration);
        songSlider.setValue(0);
        songSlider.putClientProperty("JSlider.isFilled", Boolean.TRUE);
        songSlider.putClientProperty("Slider.paintValue", Boolean.FALSE);
        Hashtable<Integer, JComponent> labels = new Hashtable<Integer, JComponent>();
        for (int i = 0; i < duration; i += 60) {
            labels.put(i, new JLabel((i / 60) + " min"));
        }
        labels.put(duration, new JLabel((duration / 60) + ":" + (duration % 60)));
        songSlider.setLabelTable(labels);

        boolean hasGain = player.hasGainControl();
        volumeLabel.setEnabled(hasGain);
        volumeSlider.setEnabled(hasGain);
        if (hasGain) {
            try {
                player.setGain(volumeSlider.getValue() / 100d);
            } catch (BasicPlayerException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
    }

    public void progress(int bytesread, long microseconds, byte[] pcmdata, final Map properties) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                Long position = (Long)properties.get("mp3.position.microseconds");
                if (position != null) {
                    songSlider.setValue(Math.round(position / 1000000));
                }
            }
        });
    }

    public void stateUpdated(final BasicPlayerEvent bpe) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                int code = bpe.getCode();
                if (code == BasicPlayerEvent.PLAYING || code == BasicPlayerEvent.RESUMED) {
                    playButton.setEnabled(false);
                    pauseButton.setEnabled(true);
                } else if (code == BasicPlayerEvent.PAUSED || code == BasicPlayerEvent.STOPPED) {
                    pauseButton.setEnabled(false);
                    playButton.setEnabled(true);
                }
            }
        });
    }

    public void setController(BasicController bc) {
    }


    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        songLabel = new javax.swing.JLabel();
        playButton = new javax.swing.JButton();
        pauseButton = new javax.swing.JButton();
        songSlider = new javax.swing.JSlider();
        volumeLabel = new javax.swing.JLabel();
        volumeSlider = new javax.swing.JSlider();

        songLabel.setText(org.openide.util.NbBundle.getMessage(JavaZoomPlayer.class, "JavaZoomPlayer.songLabel.text", new Object[] {"---"})); // NOI18N
        songLabel.setEnabled(false);

        playButton.setText(org.openide.util.NbBundle.getMessage(JavaZoomPlayer.class, "JavaZoomPlayer.playButton.text")); // NOI18N
        playButton.setEnabled(false);
        playButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                playButtonActionPerformed(evt);
            }
        });

        pauseButton.setText(org.openide.util.NbBundle.getMessage(JavaZoomPlayer.class, "JavaZoomPlayer.pauseButton.text")); // NOI18N
        pauseButton.setEnabled(false);
        pauseButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                pauseButtonActionPerformed(evt);
            }
        });

        songSlider.setMajorTickSpacing(60);
        songSlider.setMinorTickSpacing(10);
        songSlider.setPaintLabels(true);
        songSlider.setPaintTicks(true);
        songSlider.setEnabled(false);
        songSlider.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                songSliderStateChanged(evt);
            }
        });

        volumeLabel.setText(org.openide.util.NbBundle.getMessage(JavaZoomPlayer.class, "JavaZoomPlayer.volumeLabel.text", new Object[] {})); // NOI18N
        volumeLabel.setEnabled(false);

        volumeSlider.setValue(75);
        volumeSlider.setEnabled(false);
        volumeSlider.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                volumeSliderStateChanged(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(songSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 505, Short.MAX_VALUE)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(playButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(pauseButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 214, Short.MAX_VALUE)
                        .addComponent(volumeLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(volumeSlider, javax.swing.GroupLayout.PREFERRED_SIZE, 97, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(songLabel))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(songLabel)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(songSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(playButton)
                        .addComponent(pauseButton)
                        .addComponent(volumeLabel))
                    .addComponent(volumeSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
    }// </editor-fold>//GEN-END:initComponents

    private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playButtonActionPerformed
        try {
            if (player.getStatus() == BasicPlayer.PAUSED) {
                player.resume();
            } else {
                player.play();
            }
        } catch (BasicPlayerException ex) {
            Exceptions.printStackTrace(ex);
        }
    }//GEN-LAST:event_playButtonActionPerformed

    private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed
        if (player.getStatus() == BasicPlayer.PLAYING) {
            try {
                player.pause();
            } catch (BasicPlayerException ex) {
                Exceptions.printStackTrace(ex);
            }
        }
    }//GEN-LAST:event_pauseButtonActionPerformed

    private void songSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_songSliderStateChanged
        boolean isAdjusting = songSlider.getValueIsAdjusting();
        if (sliderDragged == isAdjusting) {
            // not a start nor finish of slider drag
            return;
        }
        try {
            if (!sliderDragged && isAdjusting) { // drag started
                wasPlaying = player.getStatus() == BasicPlayer.PLAYING;
                if (wasPlaying) {
                    player.pause();
                }


            }
            if (sliderDragged && !isAdjusting) { // drag finished
                long bytesToSkip = Math.round(songSlider.getValue() * bytesPerSecond);
                System.out.println("drag finished, bytes to skip:" + bytesToSkip);
                player.seek(bytesToSkip);
                if (wasPlaying) {
                    player.resume();
                }
            }
        } catch (BasicPlayerException ex) {
            Exceptions.printStackTrace(ex);
        }

        sliderDragged = isAdjusting;

    }//GEN-LAST:event_songSliderStateChanged

    private void volumeSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_volumeSliderStateChanged
        try {
            player.setGain(volumeSlider.getValue() / 100d);
        } catch (BasicPlayerException ex) {
            Exceptions.printStackTrace(ex);
        }
    }//GEN-LAST:event_volumeSliderStateChanged


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton pauseButton;
    private javax.swing.JButton playButton;
    private javax.swing.JLabel songLabel;
    private javax.swing.JSlider songSlider;
    private javax.swing.JLabel volumeLabel;
    private javax.swing.JSlider volumeSlider;
    // End of variables declaration//GEN-END:variables



}
